namespace EFS.Common.Values.Generic
{
	/// Value generic class
	public abstract class Value<ValueType> : EFS.Common.Values.Interfaces.Value
	{
		#region Composition

		/// used to store a date time date type
		private System.Object innerValue = null;

		/// <summary>
		/// Indicator
		/// </summary>
		private System.Boolean isNullSpecified = false;

		#endregion

		#region Constructors

		/// Constructor
		public Value()
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="innerValue"></param>
		public Value(ValueType innerValue)
		{
			this.innerValue = innerValue;
		}

		#endregion

		#region Content manipulation

		/// Default
		public virtual System.Object Default
		{
			get
			{
				return default(ValueType);
			}
		}

		/// Property InnerObjectType
		public System.Type InnerObjectType
		{
			get
			{
				return typeof(ValueType);
			}
		}

		/// IsAssignableFrom
		public virtual System.Boolean IsAssignableFrom(System.Type c)
		{
			return InnerObjectType.IsAssignableFrom(c);
		}

		/// Property Inner Object
		public virtual System.Object InnerObject
		{
			get
			{
				return this.innerValue;
			}
			set
			{
				// check if input not null
				if (null != value)
				{
					// check if expected type
					EFS.Common.Verify.ExpectedType(value, InnerObjectType);
				}

				// set innerValue
				this.innerValue = value;

				// reset null specified flag if not null value set 
				if (!IsNull) { isNullSpecified = false; }
			}
		}

		/// Property ValueType
		public ValueType InnerValue
		{
			get
			{
				if (this.IsNull)
				{

					return default(ValueType);
				}

				return (ValueType)this.innerValue;
			}
			set
			{
				// set innerValue
				this.innerValue = value;

				// reset null specified flag if not null value set 
				if (!IsNull) { isNullSpecified = false; }
			}
		}

		/// Null property: Get
		public System.Int32 Indicator
		{
			get
			{
				return (!IsNull) || (IsNull && IsNullSpecified) ? 0 : -1;
			}
		}

		/// Null value indicator
		public System.Boolean IsNullSpecified
		{
			get { return isNullSpecified; }
		}

		/// <summary>
		/// Set null
		/// </summary>
		public void SetNullSpecified()
		{
			innerValue = null;
			isNullSpecified = true;
		}

		/// Null value 
		public System.Boolean IsNull
		{
			get { return null == innerValue; }
		}

		/// Reset value
		public void SetNull()
		{
			innerValue = null;
			isNullSpecified = false;
		}

		/// Copy value
		public virtual void Copy(EFS.Common.Values.Interfaces.Value value)
		{
 			// check
			EFS.Common.Verify.Reference(value, "value");
			EFS.Common.Verify.ExpectedType(value, GetType());
			
			// check null
			if (value.IsNull)
			{
				// set null
				SetNull();
			}
			else
			{
				// write value
				InnerObject = value.InnerObject;
			}
		}

		#endregion

		#region Serializable

		/// <summary>
		/// Serialize
		/// </summary>
		/// <param name="xmlWriter"></param>
		public virtual void Serialize(EFS.Common.Serialization.XmlWriter xmlWriter)
		{
			// read indicator
			xmlWriter.Write("null", isNullSpecified);

			// check indicator
			if (!IsNullSpecified)
			{
				// read value element
				xmlWriter.Write("value", ToString());
			}
		}

		/// <summary>
		/// Deserialize
		/// </summary>
		/// <param name="xmlReader"></param>
		public virtual void Deserialize(EFS.Common.Serialization.XmlReader xmlReader)
		{
			// reset
			SetNull();

			// read indicator
			xmlReader.Read("null", ref isNullSpecified);

			// check indicator
			if (!IsNullSpecified)
			{
				// read value element
				SetString(xmlReader.Read("value"));
			}
		}

		#endregion

		#region IComparable

		/// <summary>
		/// Compare
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public System.Int32 CompareTo(object obj)
		{
			// check
			EFS.Common.Verify.ExpectedType(obj, this.GetType());

			// generic
			EFS.Common.Values.Generic.Value<ValueType> generig = obj as EFS.Common.Values.Generic.Value<ValueType>;

			// check null
			if (IsNull)
			{
				return generig.IsNull ? 0 : 1;
			}

			// compare values
			return (innerValue as System.IComparable).CompareTo(generig.innerValue);
		}

		#endregion

		#region String conversion

		// set as string
		public abstract void SetString(System.String valueAsString);

		// To String
		public override string ToString()
		{
			// check null
			if (!IsNull)
			{
				return innerValue.ToString();
			}

			// empty
			return "";
		}

		#endregion

		/// Retrieves the safe value
		public ValueType GetSafeDefault(ValueType @default)
		{
			// return default
			if (IsNull)
			{
				return @default;
			}

			// ok
			return InnerValue;
		}

		/// Safe equals
		public System.Boolean EqualsSafe(ValueType value)
		{
			// return default
			if (IsNull)
			{
				return false;
			}

			// ok
			return InnerValue.Equals(value);
		}
	}

}